<?php
/**
 * Created by PhpStorm.
 * User: lhl
 * Date: 2020/11/30
 * Time: 14:35
 * Features: 升级程序,迁移学习数据
 */

namespace app\timeexecution\command;

use app\common\model\access_log\HostAccessLogModel;
use app\common\model\learn_data\HostLearnDataModel;
use think\console\Input;
use think\console\Output;
use think\Db;
use think\Exception;
use think\facade\Config;

class LearnData extends Base
{
    /**
     * 配置命令
     */
    protected function configure()
    {
        $this->setName('LearnData')->setDescription('learn data handle command');
    }

    /**
     * 执行程序
     */
    protected function execute(Input $input, Output $output)
    {
        set_time_limit(0);


        try{
            $this->writeAndLogs('start execute learn_data',$output, false);

            //修改协议
            $tableModifyName = ['host_rule', 'host_port', 'host_guise_port', 'virtual_rule'];
            foreach ($tableModifyName as $tableModify){
                Db::name($tableModify)->where('protocols', 1)->update(['protocols' => 6]);
                Db::name($tableModify)->where('protocols', 2)->update(['protocols' => 17]);
                Db::name($tableModify)->where('protocols', 3)->update(['protocols' => 255]);
            }


            $hostLearnDataModelServer = new HostLearnDataModel();
            $hostAccessLogModelServer = new HostAccessLogModel();

            //0.判断原表是否存在
            $tableName = 'host_learn_data';
            $fullTableName = Config::get('database.prefix') . $tableName;
            //表不存在,终止运行
            if (!Db::query("SHOW TABLES LIKE '{$fullTableName}'")) {
                $this->writeAndLogs("table {$fullTableName} is not exist,maybe you have already upgraded",$output, true);
            }

            $accesslogTableName = 'host_access_log';
            $accessLogFullTableName = Config::get('database.prefix') . $accesslogTableName;

            //表不存在,终止运行
            if (!Db::query("SHOW TABLES LIKE '{$accessLogFullTableName}'")) {
                $this->writeAndLogs("table {$accessLogFullTableName} is not exist,maybe you have already upgraded",$output, true);
            }

            //1. 查询数据
            $hostidAllList = Db::name('host')->column('id');

            //如果有主机,进行数据迁移
            if($hostidAllList){
                $hostidGroupList = [];
                //根据host_id分组
                foreach ($hostidAllList as $k => $hostid){
                    $node = $hostLearnDataModelServer->getDataBaseNo($hostid);
                    $hostidGroupList[$node][] = $hostid;
                }

                //新建数据库
                foreach ($hostidGroupList as $group){
                    $res = $hostLearnDataModelServer->createChildDatabaseByRoot($group[0]);
                    //创库失败,直接终止程序，返回错误信息
                    if(!$res['result']){
                        $this->writeAndLogs("create database {$res['db']['name']} fail,reason：{$res['msg']}",$output, true);
                    }

                    //创库失败,直接终止程序，返回错误信息
                    $res = $hostAccessLogModelServer->createChildDatabaseByRoot($group[0]);
                    if(!$res['result']){
                        $this->writeAndLogs("create database {$res['db']['name']} fail,reason：{$res['msg']}",$output, true);
                    }

                }

                //数据处理
                foreach ($hostidGroupList as $hostArr){
                    foreach ($hostArr as $hostId){
                        //建表
                        $createRes = $hostLearnDataModelServer->createTable($hostId);
                        $createRes1 = $hostAccessLogModelServer->createTable($hostId);
                        if(!$createRes){
                            $this->writeAndLogs('create learn_data tables error:host_id=>'.$hostId, $output,false);
                            continue;
                        }

                        if(!$createRes1){
                            $this->writeAndLogs('create access_log tables error:host_id=>'.$hostId, $output,false);
                            continue;
                        }

                        $learnField = "host_id, if(protocols  = 1 ,'6','17') as protocols, src_ip,
                         src_mac, dest_ip, dest_port, dest_mac, direction, view, last_view, status,
                          have_mac_addr, mark, ctime, matched";

                        //读取学习数据
                        $hostIdData = Db::name($tableName)
                            ->where('host_id', $hostId)
                            ->field($learnField)
                            ->whereIn('protocols', [1,2])
                            ->select();

                        //迁移学习数据
                        $hostLearnDataModelServer->setHostId($hostId);
                        $hostLearnDataModelServer->insertAll($hostIdData, true, 1000);

                        //读取防火墙日志
                        $logField = "host_id, `source_ip`, `target_ip`, `direction`, `target_port`, 
                         if(protocols  = 1 ,'6','17') as protocols, `permission`, `first_time`,
                         `last_time`, `ignore`, `guise`, `view_all`, `syslog`, `elk`";

                        $hostIdAccessLogData = Db::name($accesslogTableName)
                            ->where('host_id', $hostId)
                            ->field($logField)
                            ->whereIn('protocols', [1,2])
                            ->select();

                        //迁移防火墙日志数据
                        $hostAccessLogModelServer->setHostId($hostId);
                        if(!empty($hostIdAccessLogData)){
                            $hostAccessLogModelServer->insertAll($hostIdAccessLogData, true, 1000);
                        }
                    }

                    //关闭连接
                    $hostLearnDataModelServer->closeConnection();
                    $hostAccessLogModelServer->closeConnection();

                }
            }



            //重命名学习表
            $renameTable = $fullTableName.'_old';
            $renameAccessLogTable = $accessLogFullTableName.'_old';

            $renameSql = "RENAME TABLE $fullTableName TO $renameTable;";
            $renameAccessLogSql = "RENAME TABLE $accessLogFullTableName TO $renameAccessLogTable;";

            Db::execute($renameSql);
            Db::execute($renameAccessLogSql);

            $this->writeAndLogs('end excute learn_data', $output, true);
        }catch (Exception $exception){
            $this->writeAndLogs('Program error: '.$exception->getMessage(), $output, true);
        }catch (\Exception $exception){
            $this->writeAndLogs('Program error: '.$exception->getMessage(), $output, true);
        }

    }

}